package cn.flant.exec;

import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public final class ExecutorPool {

	private static ScheduledThreadPoolExecutor scheduleExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1);
	private static Hashtable<Object, ScheduledFuture<?>> scheduleFutureMap = new Hashtable<Object, ScheduledFuture<?>>();
	private static ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
	static {
		Runtime.getRuntime().addShutdownHook(new Thread() {
			public void run() {
				shutdownNow();
			}
		});
	}

	private ExecutorPool() {
	}

	public static void setSchedulePoolSize(int poolSize) {
		if (poolSize <= 0 || poolSize == scheduleExecutor.getCorePoolSize()) {
			return;
		}
		scheduleExecutor.setCorePoolSize(poolSize);
	}

	public static int getSchedulePoolSize() {
		return scheduleExecutor.getCorePoolSize();
	}

	public static ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
		ScheduledFuture<?> future = scheduleExecutor.schedule(command, delay, unit);
		scheduleFutureMap.put(command, future);
		return future;
	}

	public static <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
		ScheduledFuture<V> future = scheduleExecutor.schedule(callable, delay, unit);
		scheduleFutureMap.put(callable, future);
		return future;
	}

	public static ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
		ScheduledFuture<?> future = scheduleExecutor.scheduleAtFixedRate(command, initialDelay, period, unit);
		scheduleFutureMap.put(command, future);
		return future;
	}

	public static ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
		ScheduledFuture<?> future = scheduleExecutor.scheduleWithFixedDelay(command, initialDelay, delay, unit);
		scheduleFutureMap.put(command, future);
		return future;
	}

	public static boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
		boolean bool = executor == null ? true : executor.awaitTermination(timeout, unit);
		bool = bool && scheduleExecutor.awaitTermination(timeout, unit);
		return bool;
	}

	public static <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks) throws InterruptedException {
		return executor.invokeAll(tasks);
	}

	public static <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
		return executor.invokeAll(tasks, timeout, unit);
	}

	public static <T> T invokeAny(Collection<Callable<T>> tasks) throws InterruptedException, ExecutionException {
		return executor.invokeAny(tasks);
	}

	public static <T> T invokeAny(Collection<Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException,
			TimeoutException {
		return executor.invokeAny(tasks, timeout, unit);
	}

	public static int prestartAllCoreThreads() {
		return scheduleExecutor.prestartAllCoreThreads();
	}

	public static boolean prestartCoreThread() {
		return scheduleExecutor.prestartCoreThread();
	}

	public static void shutdown() {
		scheduleExecutor.shutdown();
		executor.shutdown();
	}

	public static List<Runnable> shutdownNow() {
		List<Runnable> list = scheduleExecutor.shutdownNow();
		list.addAll(executor.shutdownNow());
		return list;
	}

	public static <T> Future<T> submit(Callable<T> task) {
		return executor.submit(task);
	}

	public static Future<?> submit(Runnable task) {
		return executor.submit(task);
	}

	public static <T> Future<T> submit(Runnable task, T result) {
		return executor.submit(task, result);
	}

	public static void execute(Runnable command) {
		executor.execute(command);
	}

	public static void remove(Runnable command) {
		if (!executor.remove(command)) {
			scheduleExecutor.remove(command);
			cancelSchedule(command);
		}
	}

	public static void cancelSchedule(Runnable command) {
		ScheduledFuture<?> future = scheduleFutureMap.remove(command);
		if (future != null) {
			future.cancel(false);
		}
	}

	public static void cancelSchedule(Callable<?> callable) {
		ScheduledFuture<?> future = scheduleFutureMap.remove(callable);
		if (future != null) {
			future.cancel(false);
		}
	}

	public static boolean isScheduled(Object runner) {
		return scheduleFutureMap.containsKey(runner);
	}
}
